<?php

namespace Respect\StackedModularDispatcher;

class FakeClassMain
{

    public $callOrder = array();

    public function setupGet()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function finishGet()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function handleGet()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function handleGetCacheAuth()
    {
        $this->callOrder[__FUNCTION__] = microtime();
        return $this->extra;
    }

}

class FakeClassMain2
{

    public $callOrder = array();

    public function setupGetFrom()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function finishGetFrom()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function handleFrom()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

}

class FakeClassCache
{

    public $callOrder = array();

    public function setupCache()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function finishCache()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

}

class FakeClassAuth
{

    public $callOrder = array();

    public function setupAuth()
    {
        $this->callOrder[__FUNCTION__] = microtime();
        return $this->extra;
    }

    public function finishAuth()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

}

class FakeClassFrom
{

    public $callOrder = array();

    public function setupFrom()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

    public function finishFrom()
    {
        $this->callOrder[__FUNCTION__] = microtime();
    }

}

class MethodsAsModulesTest extends \PHPUnit_Framework_TestCase
{

    protected $object;

    protected function setUp()
    {
        $this->object = new Dispatcher(
                new \Respect\SimpleDataContainer\Container(),
                function($module) {
                    return $module;
                }
        );
    }

    protected function tearDown()
    {
        unset($this->object);
    }

    public function testAppendSingleInstance()
    {
        $fakeInstance = new \stdClass;
        $this->object->appendInstance($fakeInstance);
        $this->assertEquals(array($fakeInstance), $this->object->getInstances());
    }

    public function testAppendMultipleInstances()
    {
        $fakeInstanceLast = new FakeClassMain;
        $fakeInstanceCache = new FakeClassCache;
        $fakeInstanceAuth = new FakeClassAuth;
        $this->object->appendInstance($fakeInstanceLast);
        $this->object->appendInstance($fakeInstanceCache);
        $this->object->appendInstance($fakeInstanceAuth);
        $this->assertEquals(
            array(
                $fakeInstanceCache,
                $fakeInstanceAuth,
                $fakeInstanceLast
            ),
            $this->object->getInstances()
        );
    }

    public function testAppendMultipleInstancesOrder()
    {
        $fakeInstanceLast = new FakeClassMain;
        $fakeInstanceCache = new FakeClassCache;
        $fakeInstanceAuth = new FakeClassAuth;
        $this->object->appendInstance($fakeInstanceLast);
        $this->object->appendInstance($fakeInstanceAuth);
        $this->object->appendInstance($fakeInstanceCache);
        $this->assertEquals(
            array(
                $fakeInstanceAuth,
                $fakeInstanceCache,
                $fakeInstanceLast
            ),
            $this->object->getInstances()
        );
    }

    public function testLoadModule()
    {
        $this->assertEquals('fooBar', $this->object->loadModule('fooBar'));
        $this->assertEquals('fooBarBaz', $this->object->loadModule('fooBarBaz'));
    }

    public function testRunDirty()
    {
        $fakeInstanceLast = new FakeClassMain;
        $fakeInstanceLast2 = new FakeClassMain2;
        $fakeInstanceCache = new FakeClassCache;
        $fakeInstanceAuth = new FakeClassAuth;
        $fakeInstanceFrom = new FakeClassFrom;
        $this->object->appendInstance($fakeInstanceLast);
        $this->object->appendInstance($fakeInstanceCache);
        $this->object->appendInstance($fakeInstanceAuth);
        $fakeInstanceAuth->extra = $fakeInstanceFrom; //will replace instance
        $fakeInstanceLast->extra = $fakeInstanceLast2; //will replace instance
        $this->object->run();
        //merge call microtimes
        $callOrder = array_merge(
                $fakeInstanceLast->callOrder,
                $fakeInstanceCache->callOrder,
                $fakeInstanceAuth->callOrder,
                $fakeInstanceFrom->callOrder,
                $fakeInstanceLast2->callOrder
        );
        //sort them
        natsort($callOrder);
        //get method order
        $callOrder = array_keys($callOrder);
        $this->assertEquals(
            array(
                'setupCache',
                'setupAuth',
                'setupGet',
                'handleGetCacheAuth', //will be aborted
                'setupGetFrom', //replaced main setup
                'handleFrom', //replaced main handle
                'finishGetFrom', //replaced main handle
                'finishFrom', //replaced auth
                'finishCache'
            ),
            $callOrder
        );
        $this->assertNotContains('hangleGet', $callOrder);
    }

}